﻿#region Namespace References

using System;
using System.Collections.Generic;
using System.Data;
using DynConDevCorp.OpenArch.SmartData.Core;
using DynConDevCorp.OpenArch.SmartData.Interfaces;
using DynConDevCorp.OpenArch.SmartData.Interfaces.MetaData;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Persistance
{
    /// <summary>
    /// 
    /// </summary>
    public class SmartLoader : ISmartDispatcher
    {
        //private void CreateRelationships()
        //{
        //    if (DataSource != null)
        //    {
        //        foreach (DataRelation relation in DataSource.Relations)
        //        {
        //            int x = 1;
        //        }
        //    }
        //}

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SmartLoader"/> class.
        /// </summary>
        /// <param name="loaderSettings">The loader settings.</param>
        public SmartLoader(SmartLoaderSettings loaderSettings)
        {
            m_LoaderSettings = loaderSettings;
        }

        #endregion

        #region Public Instance Properties

        /// <summary>
        /// Gets or sets the data source.
        /// </summary>
        /// <value>The data source.</value>
        public DataSet DataSource
        {
            get { return m_DataSource; }
            set
            {
                m_DataSource = value;
                // CreateRelationships(); 
            }
        }

        #endregion

        #region Public Instance Methods

        /// <summary>
        /// Creates the meta entity.
        /// </summary>
        /// <param name="table">The table.</param>
        /// <returns></returns>
        public ISmartMetaEntity CreateMetaEntity(DataTable table)
        {
            ISmartMetaEntity metaEntity = SmartFactory.GlobalInstance.CreateMetaEntityInstance();
            foreach (DataColumn col in table.Columns)
            {
                string colName = col.ColumnName;
                Type colType = col.DataType;
                Type rootSmartType = (colType.IsValueType ? typeof (SmartNullableField<int>) : typeof(SmartField<int>)).GetGenericTypeDefinition();
                Type actualFieldType = rootSmartType.MakeGenericType(new[] { colType });
                metaEntity.DeclareField(colName, actualFieldType);
            }
            return metaEntity;
        }

        /// <summary>
        /// Loads the table.
        /// </summary>
        /// <param name="persistanceMetadata">The persistance metadata.</param>
        /// <param name="table">The table.</param>
        /// <param name="collection">The collection.</param>
        private void LoadTable(PersistanceMetadata persistanceMetadata, DataTable table, 
                              ISmartEntityCollection collection)
        {
            foreach (DataRow row in table.Rows)
            {
                ISmartEntity entity = LoadEntity(persistanceMetadata.EntityType, persistanceMetadata.DatabaseToObjectMap(), table, row);
                collection.Add(entity);
            }
        }

        /// <summary>
        /// Loads the table.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <param name="table">The table.</param>
        /// <param name="metaEntity">The meta entity.</param>
        /// <param name="collection">The collection.</param>
        private void LoadTable<TEntity>(DataTable table, ISmartMetaEntity metaEntity,
                                       ISmartEntityCollection<TEntity> collection)
            where TEntity : ISmartEntity, new()
        {
            PersistanceMetadata persistanceMetadata = PersistanceMetadataManager.GlobalInstance.GetMetadata<TEntity>();
            EntityFieldMap fieldMap = persistanceMetadata.DatabaseToObjectMap();
            foreach (DataRow row in table.Rows)
            {
                ISmartEntity entity = LoadEntity<TEntity>(fieldMap, table, row);
                collection.Add(entity);
            }
        }


  
        #endregion

        #region Private Instance Methods

        /// <summary>
        /// Gets the collection.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <returns></returns>
        public ISmartEntityCollection<TEntity> GetCollection<TEntity>()
            where TEntity : ISmartEntity
        {
            ISmartEntityCollection<TEntity> retVal;
            ISmartEntityCollection raw;
            Type entityType = typeof(TEntity);
            if (m_LoadedCollections.TryGetValue(entityType, out raw))
            {
                retVal = (ISmartEntityCollection<TEntity>)raw;
            }
            else
            {
                retVal = (ISmartEntityCollection<TEntity>)SmartFactory.GlobalInstance.CreateEntityCollection(entityType);
                DataTable table = DataSource.Tables[m_LoaderSettings.GetTableName(entityType)];
                PersistanceMetadata metaEntity = m_LoaderSettings.GetMetaEntity(entityType);
                LoadTable(metaEntity, table, retVal);
                m_LoadedCollections.Add(entityType, retVal);
            }
            return retVal;
        }


        /// <summary>
        /// Gets the collection.
        /// </summary>
        /// <typeparam name="TEntity">The type of the entity.</typeparam>
        /// <returns></returns>
        public ISmartEntityCollection<TEntity> GetCollection<TEntity>(DataTable table)
            where TEntity : ISmartEntity
        {
            ISmartEntityCollection<TEntity> retVal;
            ISmartEntityCollection raw;
            Type entityType = typeof(TEntity);
            if (m_LoadedCollections.TryGetValue(entityType, out raw))
            {
                retVal = (ISmartEntityCollection<TEntity>)raw;
            }
            else
            {
                retVal = (ISmartEntityCollection<TEntity>)SmartFactory.GlobalInstance.CreateEntityCollection(entityType);
                PersistanceMetadata metaEntity = m_LoaderSettings.GetMetaEntity(entityType);
                LoadTable(metaEntity, table, retVal);
                m_LoadedCollections.Add(entityType, retVal);
            }
            return retVal;
        }

        /// <summary>
        /// Gets the collection.
        /// </summary>
        /// <param name="entityType">Type of the entity.</param>
        /// <returns></returns>
        public ISmartEntityCollection GetCollection(Type entityType)
        {
            ISmartEntityCollection retVal;
            if (!m_LoadedCollections.TryGetValue(entityType, out retVal))
            {
                retVal = SmartFactory.GlobalInstance.CreateEntityCollection(entityType);
                DataTable table = DataSource.Tables[m_LoaderSettings.GetTableName(entityType)];
                PersistanceMetadata metaEntity = m_LoaderSettings.GetMetaEntity(entityType);
                LoadTable(metaEntity, table, retVal);
                m_LoadedCollections.Add(entityType, retVal);
            }
            return retVal;
        }

        private ISmartEntity LoadEntity(Type entityType, EntityFieldMap fieldMap, DataTable table, DataRow row)
        {
            ISmartEntity entity = SmartFactory.GlobalInstance.CreateEntityInstance(entityType);
            return LoadEntity(fieldMap, entity, table, row);
        }

        /// <summary>
        /// Loads the entity.
        /// </summary>
        /// <param name="fieldMap">The field map.</param>
        /// <param name="entity">The entity.</param>
        /// <param name="table">The table.</param>
        /// <param name="row">The row.</param>
        /// <returns></returns>
        private ISmartEntity LoadEntity(EntityFieldMap fieldMap, ISmartEntity entity, DataTable table, DataRow row)
        {
            foreach (FieldMapEntry entry in fieldMap)
            {
                if (table.Columns.Contains(entry.PersistanceFieldName))
                {
                    object rawValue = row[entry.PersistanceFieldName];
                    if (rawValue == DBNull.Value)
                        rawValue = null;
                    entity[entry.ObjectFieldName].RawValue = rawValue;
                }
            }
            ResolveRelationships(entity);
            return entity;
        }

        private ISmartEntity LoadEntity<TEntity>(EntityFieldMap fieldMap, DataTable table, DataRow row)
            where TEntity : ISmartEntity
        {
            return LoadEntity(typeof(TEntity), fieldMap, table, row);
        }

        private void ResolveRelationships(ISmartEntity entity)
        {
            SmartLoaderSettings.RelationshipList relationships = m_LoaderSettings.GetRelationships(entity.GetType());
            foreach (SmartLoaderSettings.Relationship relationship in relationships)
            {
                string childKeyValue = entity[relationship.ChildKeyField].RawValue.ToString();
                Type parentType = relationship.ParentType;
                ISmartField childReferenceField = entity[relationship.ChildReferenceField];
                          ISmartEntityLink childFieldEntityLink = childReferenceField as ISmartEntityLink;
              ISmartEntityCollection parentCollection = GetCollection(parentType);
                foreach (ISmartEntity possibleParent in parentCollection)
                {
                    string parentKeyValue = possibleParent[relationship.ParentKeyField].ToString();
                    if (childKeyValue==parentKeyValue)
                    {
                        ISmartEntity parentInstance = possibleParent;
                        if (childFieldEntityLink != null)
                        {
                            childFieldEntityLink.SetRawLinkedEntity(parentInstance);
                        }
                        else
                        {
                            childReferenceField.RawValue = parentInstance;
                        }
                    }
                }
            }
        }

        #endregion

        #region Fields and Constants

        private readonly Dictionary<Type, ISmartEntityCollection> m_LoadedCollections =
            new Dictionary<Type, ISmartEntityCollection>();

        private readonly SmartLoaderSettings m_LoaderSettings;
        private DataSet m_DataSource;

        #endregion

         #region Implementation of ISmartDispatcher

        void ISmartDispatcher.ProcessDispatch(ISmartEntity target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntityCollection target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartEntityLink target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartField target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartNullableField target, object context)
        {
            throw new NotImplementedException();
        }

        void ISmartDispatcher.ProcessDispatch(ISmartDisplayFormatter target, object context)
        {
            throw new NotImplementedException();
        }
        #endregion
    }
}